package com.pangu.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import com.alibaba.fastjson.JSON;
import com.pangu.util.rules.model.NiuniuPosition;
import com.pangu.util.rules.model.PokerCard;
import com.pangu.util.rules.model.PokerPatterns;

import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName: PokerUtilsPro
 * @Description: 德州比较升级版
 *               <p>
 *               几点要求：牌排序一定是从大到小
 *               <p>
 *               皇家同花顺9 >> 同花顺8 >> 四条7 >> 葫芦6 >> 同花5 >> 顺子4 >> 三条3 >> 两对2 >>
 *               一对1>> 高牌0
 * 
 * @author 老王
 * @date 2018年5月29日 下午4:45:15
 *
 */
/**
 * @ClassName: PokerUtilsPro
 * @Description: 牌型处理2.0
 * @author 老王
 * @date 2018年5月31日 下午6:42:22
 *
 */
@Slf4j
public class PokerUtilsPro {

	private static final int shuffletimes = 20;// 每次洗牌

	/**
	 * @Description :得到公牌
	 * @author 老王
	 * 
	 */
	public static byte[] getCommonCard(byte[] cards) {
		byte[] commonCard = new byte[5];
		int j = 14;
		for (int i = 0; i < commonCard.length; i++) {
			commonCard[i] = cards[j];
			j++;
		}
		return commonCard;
	}

		/**
			 * @Description :洗出指定用户输的牌型
			 * @author 老王
			 */
	public static byte[] shufflePlayerLosesr(NiuniuPosition[] p, String player) {

		return null;

	}

	/**
	 * @Description :随机一个ai赢,返回洗好的公牌。发牌之前进行测试
	 * @author 老王
	 *         <p>
	 *         若没有AI则不作任何改变。
	 *         <p>
	 *         请不要改变p的值,
	 *         <p>
	 * @FIXME 控制洗牌次数，以免进入到死循环，比如只运行洗牌10W次
	 * 
	 */
	public static byte[] shuffleAiWinner(NiuniuPosition[] p) {
		// 是否有ai。若没有 则返回随机牌
		boolean flag = false;
		for (NiuniuPosition b : p) {
			if (b.isAi()) {
				flag = true;
				break;
			}
		}
		if (!flag) {
			return shuffle();
		}
		// 深度拷贝
		NiuniuPosition[] temp = new NiuniuPosition[p.length];
		for (int i = 0; i < temp.length; i++) {
			temp[i] = (NiuniuPosition) p[i].clone();
		}
		// 发牌
		byte[] cards = shuffle();
		for (int i = 0; i < temp.length; i++) {
			temp[i].setCards(new byte[2]);
		}
		for (int i = 0; i < 9 * 2; i++) {
			int pos = i % 9;
			temp[pos].getCards()[i / 9] = cards[i];
		}
		byte[] commonCard = getCommonCard(cards);
		NiuniuPosition[] np = PokerUtilsPro.Compare(temp, commonCard);
		List<NiuniuPosition> winner = PokerUtilsPro.getWiner(temp);
		flag = false;
		for (NiuniuPosition niuniuPosition : winner) {
			if (niuniuPosition.isAi()) {
				flag = true;
				break;
			}
		}
		if (!flag) {
			shuffleAiWinner(p);
		}
		return cards;
	}

	/**
	 * @Description :中途洗牌,
	 * @author 老王
	 * 
	 */
	public static void Shuffle(byte[] cards) {
		System.out.println("cards.length:" + cards.length);
		List<Byte> temp = new ArrayList<Byte>();
		for (int i = 0; i < cards.length; i++) {
			temp.add(cards[i]);
		}
		for (int i = 0; i < shuffletimes; i++) {
			Collections.shuffle(temp);
		}
		for (int i = 0; i < temp.size(); i++) {
			cards[i] = temp.get(i);
		}
	}

	/**
	 * @Description :第一次洗牌,产生一副洗好的牌
	 * @author 老王
	 */
	public static byte[] shuffle() {

		List<Byte> temp = new ArrayList<Byte>();
		for (int i = 0; i < 52; i++) {
			temp.add((byte) i);
		}
		/**
		 * 洗牌次数，参数指定，建议洗牌次数 为1次，多次洗牌的随机效果更好，例如：7次
		 */
		for (int i = 0; i < shuffletimes; i++) {
			Collections.shuffle(temp);
		}

		byte[] cards = new byte[52];
		for (int i = 0; i < temp.size(); i++) {
			cards[i] = temp.get(i);
		}
		return cards;

	}

	/**
	 * @Description 比较大小，
	 *              <p>
	 *              返回对象结果:isWinner(),
	 *              <p>
	 *              排序为前五个为该牌最大的牌型
	 *              <P>
	 *              算法实现逻辑，排序，得到最大的牌型记录到实体里面，然后再比较。
	 *              <p>
	 *              注意，比较是有序的，因为顺子是属于同花顺的，
	 */
	public static NiuniuPosition[] Compare(NiuniuPosition[] card, byte[] commonCard) {

		NiuniuPosition[] p = new NiuniuPosition[card.length];
		// 得到所有的牌型
		for (int i = 0; i < card.length; i++) {
			p[i] = getPokerPatterns(commonCard, card[i]);
		}

		// 得到输赢
		NiuniuPosition winner = null;
		// 得到第一个用户
		for (int i = 1; i < p.length; i++) {
			if (p[i].getPlayer() != null && p[i].getOdds() >= 0) {
				winner = p[i];
				break;
			}
		}

		int t = 0;
		for (int i = 1; i < p.length; i++) {
			if (p[i].getPlayer() != null && p[i].getOdds() >= 0) {
				int j = Compare(winner, p[i]);
				if (j < 0) {
					winner = p[i];
					t = i;
				}
			}
		}
		p[t].setWinner(true);
		for (int i = 1; i < p.length; i++) {
			if (p[i].getPlayer() != null && p[i].getOdds() >= 0) {
				if (Compare(winner, p[i]) == 0) {
					p[i].setWinner(true);
				}

			}
		}
		return p;
	}

	/**
	 * @Description :比较两个大小
	 * @author 老王
	 *         <p>
	 * 
	 */
	public static int Compare(NiuniuPosition card1, NiuniuPosition card2) {
		if (card1.getPatterns() > card2.getPatterns()) {// 牌型不一样容易比较
			return 1;
		} else if (card1.getPatterns() < card2.getPatterns()) {// 牌型不一样容易比较
			return -1;
		}
		return CompareHigh(card1.getCards(), card2.getCards());

	}

	/**
	 * @Description :比较高牌:比较前五个牌的大小就够了
	 *              <p>
	 *              转换为点数
	 * 
	 */
	private static int CompareHigh(byte[] card1, byte[] card2) {
		int[] c1 = byteCardToPoint(card1);
		int[] c2 = byteCardToPoint(card2);
		for (int i = 0; i < 5; i++) {
			if (c1[i] > c2[i]) {
				return 1;
			} else if (c1[i] < c2[i]) {
				return -1;
			}

		}
		return 0;

	}

	/**
	 * @Description :得到牛牛牌型
	 *              <p>
	 *              我会计算输赢
	 * 
	 */
	public static NiuniuPosition getPokerPatterns(byte[] commonCard, NiuniuPosition card) {

		byte[] c = GameUtils.sortCards(ArrayUtils.addAll(commonCard, card.getCards()));
		PokerPatterns pp = getPokerCard(c);
		card.setCards(pp.getCards());
		card.setPatterns(pp.getRanking());
		return card;
	}

	/**
	 * @Description :获取牌型
	 *              <p>
	 *              穷举法。每一个去匹配
	 *              <p>
	 *              皇家同花顺9 >> 同花顺8 >> 四条7 >> 葫芦6 >> 同花5 >> 顺子4 >> 三条3 >> 两对2 >>
	 *              一对1>> 高牌0
	 *              <P>
	 * 
	 */
	public static PokerPatterns getPokerCard(byte[] b) {
		PokerPatterns pp = null;
		pp = isBigTongHuaShun(b);
		if (pp.getRanking() > 0) {
			return pp;// 皇家同花顺
		}
		pp = isTongHuaShun(b);
		if (pp.getRanking() > 0) {
			return pp;// 同花顺
		}
		pp = isShun(b);
		if (pp.getRanking() > 0) {
			return pp;// 顺子
		}
		pp = isBomb(b);
		if (pp.getRanking() > 0) {
			return pp;// 葫芦
		}
		pp = isHulu(b);
		if (pp.getRanking() > 0) {
			return pp;// 葫芦
		}
		pp = isTongHua(b);
		if (pp.getRanking() > 0) {
			return pp;// 同花
		}
		pp = isSanTiao(b);
		if (pp.getRanking() > 0) {
			return pp;// 三条
		}
		pp = isTwo(b);
		if (pp.getRanking() > 0) {
			return pp;// 两对
		}
		pp = isOne(b);
		if (pp.getRanking() > 0) {
			return pp;// 一对
		}
		pp = new PokerPatterns();
		pp.setCards(b);
		pp.setRanking(PokerCard.High_card.getIndex());
		return pp;
	}

	/**
	 * @Description :是否是四条
	 *              <p>
	 * 
	 */
	public static PokerPatterns isBomb(byte[] b) {
		PokerPatterns pp = new PokerPatterns();
		HashMap<Integer, Integer> hm = groupByPoint(b);
		List<Integer> temp = new ArrayList<>();

		for (int i : hm.keySet()) {
			if (hm.get(i) == 4) {
				temp.add(i);
			}
		}
		if (temp.size() != 1) {
			pp.setRanking(-1);
			return pp;
		}
		int n1 = temp.get(0);
		byte[] result = new byte[4];
		int c = 0;
		for (byte cr : b) {
			int t = byteCardToPoint(cr);
			if (t == n1) {
				result[c] = cr;
				c++;
			}
		}

		byte[] rest = MyArrayUtils.subtract(b, result);
		pp.setCards(ArrayUtils.addAll(result, rest));
		pp.setRanking(PokerCard.four_card.getIndex());

		return pp;
	}

	/**
	 * @Description :是否是葫芦
	 *              <p>
	 *              有一个坑，牌型可能是 两个三个的哦 331或者322或者 3211 想想还有其他牌型没有呢？有的话后面开发者自行补上
	 *              <p>
	 *              还要考虑两个3个的排序，以及两个对子的排序，主要是处理A
	 *              <P>
	 * 
	 */
	public static PokerPatterns isHulu(byte[] b) {
		PokerPatterns pp = new PokerPatterns();
		HashMap<Integer, Integer> hm = groupByPoint(b);
		List<Integer> temp3 = new ArrayList<>();
		List<Integer> temp2 = new ArrayList<>();
		for (int i : hm.keySet()) {
			if (hm.get(i) == 3) {
				temp3.add(i);
			}
			if (hm.get(i) == 2) {
				temp2.add(i);
			}
		}

		if (temp3.size() == 2 || (temp3.size() == 1 && temp2.size() == 1) || (temp3.size() == 1 && temp2.size() == 2)) {
			int n1 = temp3.get(0);
			int n2 = temp2.size() != 0 ? temp2.get(0) : temp3.get(1);
			if (temp2.size() == 0 && n2 == 14) {// 这是 2个三个的
				int tep = n1;
				n1 = n2;
				n2 = tep;
			}
			if (temp2.size() == 2 && temp2.get(1) == 14) {
				n2 = 14;
			}

			// 分开装好一点。
			byte[] result3 = new byte[3];
			byte[] result2 = new byte[2];
			byte[] result = new byte[5];
			int c = 0;
			int c2 = 0;
			for (byte cr : b) {
				int t = byteCardToPoint(cr);
				if (t == n1) {
					result3[c] = cr;
					c++;
				}
				if (t == n2 && c2 < 2) {
					result2[c2] = cr;
					c2++;
				}
			}

			result = ArrayUtils.addAll(result3, result2);

			byte[] rest = MyArrayUtils.subtract(b, result);
			pp.setCards(ArrayUtils.addAll(result, rest));
			pp.setRanking(PokerCard.full_card.getIndex());
			return pp;
		}
		pp.setRanking(-1);
		return pp;
	}

	/**
	 * @Description :是否是三条
	 *              <p>
	 *              三条，一对，2对 有时间的话可以抽离出一个方法，核心都是分组
	 *              <p>
	 * 
	 *              <P>
	 * 
	 */
	public static PokerPatterns isSanTiao(byte[] b) {
		PokerPatterns pp = new PokerPatterns();
		HashMap<Integer, Integer> hm = groupByPoint(b);
		List<Integer> temp = new ArrayList<>();
		for (int i : hm.keySet()) {
			if (hm.get(i) == 3) {
				temp.add(i);
			}
		}
		if (temp.size() == 0) {
			pp.setRanking(-1);
			return pp;
		}
		int n1 = temp.get(0);
		byte[] result = new byte[3];
		int c = 0;
		for (byte cr : b) {
			int t = byteCardToPoint(cr);
			if (t == n1) {
				result[c] = cr;
				c++;
			}
		}
		byte[] rest = MyArrayUtils.subtract(b, result);
		pp.setCards(ArrayUtils.addAll(result, rest));
		pp.setRanking(PokerCard.Three_strip.getIndex());
		return pp;
	}

	/**
	 * @Description :是否是1对
	 *              <P>
	 *              算法可以和2对是一样的
	 */
	public static PokerPatterns isOne(byte[] b) {
		PokerPatterns pp = new PokerPatterns();
		HashMap<Integer, Integer> hm = groupByPoint(b);

		List<Integer> temp = new ArrayList<>();
		for (int i : hm.keySet()) {
			if (hm.get(i) == 2) {
				temp.add(i);
			}
		}
		if (temp.size() != 1) {
			pp.setRanking(-1);
			return pp;
		}
		int n1 = temp.get(0);
		byte[] result = new byte[2];
		int c = 0;
		for (byte cr : b) {
			int t = byteCardToPoint(cr);
			if (t == n1) {
				result[c] = cr;
				c++;
			}
		}

		byte[] rest = MyArrayUtils.subtract(b, result);
		pp.setCards(ArrayUtils.addAll(result, rest));
		pp.setRanking(PokerCard.One_couple.getIndex());

		return pp;
	}

	/**
	 * @Description :是否是两对
	 *              <p>
	 *              算法还是分组
	 * 
	 */
	public static PokerPatterns isTwo(byte[] b) {
		PokerPatterns pp = new PokerPatterns();
		HashMap<Integer, Integer> hm = groupByPoint(b);
		List<Integer> temp = new ArrayList<>();
		for (int i : hm.keySet()) {
			if (hm.get(i) == 2) {
				temp.add(i);
			}
		}
		if (temp.size() != 2) {
			pp.setRanking(-1);
			return pp;
		}
		int n1 = temp.get(0);
		int n2 = temp.get(1);
		byte[] result = new byte[4];
		int c = 0;
		for (byte cr : b) {
			int t = byteCardToPoint(cr);
			if (t == n1 || t == n2) {
				result[c] = cr;
				c++;
			}
		}

		byte[] rest = MyArrayUtils.subtract(b, result);
		pp.setCards(ArrayUtils.addAll(result, rest));
		pp.setRanking(PokerCard.Two_couple.getIndex());

		return pp;

	}

	/**
	 * @Description :根据点数分组，计算每一个牌所占的个数
	 * 
	 */
	public static HashMap<Integer, Integer> groupByPoint(byte[] b) {
		int[] card = byteCardToPoint(b);
		HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
		// 先分组統計個數
		for (int i = 0; i < card.length; i++) {
			if (!hm.containsKey(card[i])) {
				hm.put(card[i], 1);
			} else {
				hm.put(card[i], (hm.get(card[i])) + 1);
			}
		}

		return hm;

	}

	/**
	 * @Description :是否是皇家同花顺
	 *              <p>
	 *              具体逻辑：判断是同花顺子，并且最大的为A
	 *              <p>
	 * 
	 */
	private static PokerPatterns isBigTongHuaShun(byte[] cards) {
		PokerPatterns pp = isTongHuaShun(cards);
		if (pp.getRanking() == -1) {
			return pp;
		}
		if (byteCardToPoint(pp.getCards()[0]) == 14) {
			pp.setRanking(PokerCard.huang_card.getIndex());
			return pp;
		}

		return pp;
	}

	/**
	 * @Description :是否是同花顺
	 *              <p>
	 *              具体逻辑：先判断是顺子，然后看是否同花
	 *              <p>
	 *              有一个坑：先判断顺子的时候，有可能有两个点数相同，但是花色不同的牌哦
	 *              <P>
	 * 
	 */
	private static PokerPatterns isTongHuaShun(byte[] cards) {
		PokerPatterns pp = new PokerPatterns();
		byte[] clor = getTongHua(cards);
		if (clor == null) {
			pp.setRanking(-1);
			return pp;
		}

		PokerPatterns temp = isShun(clor);
		if (PokerCard.Shun_card.getIndex() == temp.getRanking()) {
			// 牌加上剩余的

			byte[] rest = MyArrayUtils.subtract(cards, temp.getCards());
			temp.setCards(ArrayUtils.addAll(temp.getCards(), rest));
			temp.setRanking(PokerCard.tong_card.getIndex());
			return temp;
		}
		// 得到所有同花

		pp.setRanking(-1);
		return pp;
	}

	/**
	 * @Description :是否是同花
	 * @author 老王
	 *         <p>
	 *         按照花色分组就可以了
	 *         <p>
	 * 
	 *         <P>
	 * 
	 */
	public static PokerPatterns isTongHua(byte[] cards) {
		PokerPatterns pp = new PokerPatterns();
		byte[] clor = getTongHua(cards);
		if (clor == null) {
			pp.setRanking(-1);
			return pp;
		}
		byte[] clor2 = new byte[5];
		System.arraycopy(clor, 0, clor2, 0, 5);
		byte[] rest = MyArrayUtils.subtract(cards, clor2);
		pp.setCards(ArrayUtils.addAll(clor, rest));
		pp.setRanking(PokerCard.flush_card.getIndex());
		return pp;
	}

	/**
	 * @Description :得到同花
	 *              <p>
	 */
	public static byte[] getTongHua(byte[] cards) {

		// 得到花色，分组
		HashMap<Integer, List<Byte>> hm = new HashMap();
		for (byte b : cards) {
			int i = byteCardIntToCardType(b);
			if (!hm.containsKey(i)) {
				List<Byte> c = new ArrayList<>();
				c.add(b);
				hm.put(i, c);
			} else {
				List<Byte> r = hm.get(i);
				r.add(b);
				hm.put(i, r);
			}
		}
		List<Byte> result = null;
		for (Integer d : hm.keySet()) {
			if (hm.get(d).size() > 4) {
				result = hm.get(d);
				break;
			}
		}
		if (result == null) {
			return null;
		}
		byte[] clor = new byte[result.size()];
		for (int i = 0; i < result.size(); i++) {
			clor[i] = result.get(i);
		}
		return clor;
	}

	/**
	 * @Description :判断是否是顺子
	 *              <p>
	 *              注意，先转换为点数.A转换为 14
	 *              <p>
	 *              这里有一个问题 就是A是k后面的 A K Q J 10
	 *              <p>
	 *              思路：转换为具体的点数（先不考虑花色），然后判断是否有顺子，拿到数字的最大数。然后去匹配原始的数据
	 *              <p>
	 *              要单独出来一下A，比较麻烦。单独写个方法
	 * 
	 */
	public static PokerPatterns isShun(byte[] c) {
		byte[] cards = dealA(c);

		PokerPatterns pp = new PokerPatterns();
		int max = 1; // 最大连续的数字个数
		int count = 1;
		for (int i = 0; i < cards.length - 1; ++i) {
			int temp = byteCardToPoint(cards[i]);
			int temp2 = byteCardToPoint(cards[i + 1]);
			if (temp == temp2) {
				continue;
			}

			if (temp == temp2 + 1) {
				count++;
				max = max > count ? max : count;
				if (max == 5) {
					pp.setRanking(PokerCard.Shun_card.getIndex());
					log.info("recoveryA:{}", cards);
					cards = recoveryA(dealShun(cards, i + 1));
					pp.setCards(cards);
					return pp;
				}
			} else {
				count = 1;
			}
		}
		pp.setRanking(-1);
		return pp;
	}

	/**
	 * @Description :单独处理一下A,编码转换，不改变cards
	 *              <p>
	 *              0->52,1->53,2->54,3->55
	 * 
	 */
	private static byte[] dealA(byte[] c) {
		byte[] cards = new byte[c.length];
		System.arraycopy(c, 0, cards, 0, c.length);
		for (int i = 0; i < cards.length; i++) {
			if (cards[i] == 0)
				cards[i] = 52;
			if (cards[i] == 1)
				cards[i] = 53;
			if (cards[i] == 2)
				cards[i] = 54;
			if (cards[i] == 3)
				cards[i] = 55;
		}
		cards = GameUtils.sortCards(cards);
		return cards;
	}

	/**
	 * @Description :单独处理一下A，编码还原，不改变cards
	 *              <p>
	 * 
	 */
	private static byte[] recoveryA(byte[] c) {
		byte[] cards = new byte[c.length];
		System.arraycopy(c, 0, cards, 0, c.length);
		for (int i = 0; i < cards.length; i++) {
			if (cards[i] == 52)
				cards[i] = 0;
			if (cards[i] == 53)
				cards[i] = 1;
			if (cards[i] == 54)
				cards[i] = 2;
			if (cards[i] == 55)
				cards[i] = 3;
		}
		return cards;
	}

	/**
	 * @Description :根据索引以及数组进行最终的德州牌型排序，前五个为最大牌型
	 *              <p>
	 *              注意，有个坑，就是有相同的牌。index 为最小牌的索引index 应该是大于或者等于5的
	 * 
	 */
	public static byte[] dealShun(byte[] cards, int index) {
		log.info("cards:", JSON.toJSON(cards));
		byte[] result = new byte[5];
		int n = 4;
		for (int i = index; i >= 0; i--) {
			if (i == index) {
				result[n] = cards[i];
				n--;
			} else if (byteCardToPoint(cards[i]) != byteCardToPoint(cards[i + 1]) && n >= 0) {

				result[n] = cards[i];
				n--;
			}
		}
		byte[] rest = MyArrayUtils.subtract(cards, result);

		result = ArrayUtils.addAll(result, rest);
		return result;
	}

	/**
	 * @Description :转换为牌的点数
	 *              <p>
	 *              注意：A代码此时应该变成14： A K Q J 10
	 * 
	 */
	public static int byteCardToPoint(byte b) {
		int result = (b - b % 4) / 4 + 1;
		if (result == 1) {
			result = 14;
		}
		return result;
	}

	/**
	 * @Description :转换为牌的点数,传入数组，得到排序后的数组
	 *              <p>
	 *              注意：A代码此时应该变成14： A K Q J 10
	 * 
	 */
	public static int[] byteCardToPoint(byte[] b) {
		int[] card = new int[b.length];
		int i = 0;
		for (byte c : b) {
			card[i] = byteCardToPoint(c);
			i++;
		}
		card = GameUtils.sortCards(card);
		return card;
	}

	/**
	 * 
	 * Description: 返回花色，0方片，1梅花，2红心，3黑桃
	 * 
	 * @author abo
	 * @date 2018年3月31日
	 * @param b
	 * @return
	 */
	public static int byteCardIntToCardType(byte b) {
		return b % 4;
	}

	/**
	 * @Description :得到赢家的list
	 */
	public static List<NiuniuPosition> getWiner(NiuniuPosition[] p) {
		List<NiuniuPosition> list = new ArrayList<NiuniuPosition>();
		for (NiuniuPosition niuniuPosition : p) {
			if (niuniuPosition.isWinner()) {
				list.add(niuniuPosition);
			}
		}
		return list;
	}

}
